12 #include <xcb/xcb_event.h>
13 #include <xcb/xcb_icccm.h>
14 #include <xcb/xcb_ewmh.h>
15 #include <cairo/cairo.h>
18 #define MAX(A, B) ((A) > (B) ? (A) : (B))
20 #define FIFO_PATH "BSPWM_FIFO"
23 #define FONT_FAMILY "sans-serif"
25 #define HORIZONTAL_PADDING 9
35 int font_size = FONT_SIZE;
36 char external_infos[BUFSIZ] = NO_VALUE;
40 int fifo_fd, dpy_fd, sel_fd;
42 xcb_connection_t *dpy;
43 xcb_ewmh_connection_t ewmh;
47 uint16_t screen_width;
48 unsigned int horizontal_padding = HORIZONTAL_PADDING;
53 double text_width(char *s)
56 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 1, 1);
57 cairo_t *cr = cairo_create(surface);
58 cairo_text_extents_t te;
59 cairo_select_font_face(cr, font_family, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
60 cairo_set_font_size(cr, font_size);
61 cairo_text_extents(cr, s, &te);
64 cairo_surface_destroy(surface);
65 /* fprintf(stderr, "%s\n", cairo_status_to_string(cairo_status(cr))); */
69 void handle_signal(int sig)
71 if (sig == SIGTERM || sig == SIGINT || sig == SIGHUP)
75 void update_window_title(void)
78 xcb_ewmh_get_utf8_strings_reply_t ewmh_txt_prop;
79 xcb_icccm_get_text_property_reply_t icccm_txt_prop;
80 uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
81 uint32_t values_reset[] = {XCB_EVENT_MASK_NO_EVENT};
84 ewmh_txt_prop.strings = NULL;
85 icccm_txt_prop.name = NULL;
87 if (xcb_ewmh_get_active_window_reply(&ewmh, xcb_ewmh_get_active_window(&ewmh, default_screen), &win, NULL) == 1
88 && (xcb_ewmh_get_wm_name_reply(&ewmh, xcb_ewmh_get_wm_name(&ewmh, win), &ewmh_txt_prop, NULL) == 1
89 || xcb_icccm_get_wm_name_reply(dpy, xcb_icccm_get_wm_name(dpy, win), &icccm_txt_prop, NULL) == 1)) {
90 if (ewmh_txt_prop.strings != NULL)
91 window_title = strdup(ewmh_txt_prop.strings);
92 else if (icccm_txt_prop.name != NULL)
93 window_title = strdup(icccm_txt_prop.name);
95 window_title = strdup(NO_VALUE);
97 xcb_change_window_attributes(dpy, curwin, XCB_CW_EVENT_MASK, values_reset);
100 xcb_generic_error_t *err = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, win, XCB_CW_EVENT_MASK, values));
104 window_title = strdup(NO_VALUE);
108 void update_desktop_name(void)
111 xcb_ewmh_get_utf8_strings_reply_t names;
112 unsigned int pos = 0, cnt = 0;
116 if (xcb_ewmh_get_current_desktop_reply(&ewmh, xcb_ewmh_get_current_desktop(&ewmh, default_screen), &cd, NULL) == 1 && xcb_ewmh_get_desktop_names_reply(&ewmh, xcb_ewmh_get_desktop_names(&ewmh, default_screen), &names, NULL) == 1) {
117 while (cnt < cd && pos < names.strings_len) {
118 pos += strlen(names.strings + pos) + 1;
121 desktop_name = strdup(names.strings + pos);
123 desktop_name = strdup(NO_VALUE);
127 void output_infos(void)
129 double left_width = text_width(desktop_name);
130 double center_width = text_width(window_title);
131 double right_width = text_width(external_infos);
132 double available_center = screen_width - (left_width + right_width + 4 * horizontal_padding);
134 int left_pos = horizontal_padding;
135 int center_pos = left_width + 2 * horizontal_padding + (available_center / 2) - (center_width / 2);
136 int right_pos = screen_width - right_width - horizontal_padding;
138 printf("^pa(%i)%s^pa(%i)%s^pa(%i)%s\n", left_pos, desktop_name, center_pos, window_title, right_pos, external_infos);
142 void handle_event(xcb_generic_event_t *evt)
144 xcb_property_notify_event_t *pne;
145 switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
146 case XCB_PROPERTY_NOTIFY:
147 pne = (xcb_property_notify_event_t *) evt;
148 if (pne->atom == ewmh._NET_CURRENT_DESKTOP || pne->atom == ewmh._NET_DESKTOP_NAMES) {
149 update_desktop_name();
151 } else if (pne->atom == ewmh._NET_ACTIVE_WINDOW) {
152 update_window_title();
154 } else if (pne->window != screen->root && (pne->atom == ewmh._NET_WM_NAME || pne->atom == XCB_ATOM_WM_NAME)) {
155 update_window_title();
163 void register_events(void)
165 xcb_generic_error_t *err;
166 uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
167 err = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, screen->root, XCB_CW_EVENT_MASK, values));
174 dpy = xcb_connect(NULL, &default_screen);
175 xcb_intern_atom_cookie_t *ewmh_cookies;
176 ewmh_cookies = xcb_ewmh_init_atoms(dpy, &ewmh);
177 xcb_ewmh_init_atoms_replies(&ewmh, ewmh_cookies, NULL);
178 screen = ewmh.screens[default_screen];
179 screen_width = screen->width_in_pixels;
180 fifo_path = getenv(FIFO_PATH);
181 mkfifo(fifo_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
182 /* http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ */
183 fifo_fd = open(fifo_path, O_RDWR | O_NONBLOCK);
184 dpy_fd = xcb_get_file_descriptor(dpy);
185 sel_fd = MAX(fifo_fd, dpy_fd) + 1;
186 desktop_name = strdup(NO_VALUE);
187 window_title = strdup(NO_VALUE);
188 font_family = strdup(FONT_FAMILY);
193 int main(int argc, char *argv[])
196 xcb_generic_event_t *evt;
197 signal(SIGTERM, handle_signal);
198 signal(SIGINT, handle_signal);
199 signal(SIGHUP, handle_signal);
202 update_desktop_name();
203 update_window_title();
207 font_family = strdup(argv[1]);
209 font_size = atoi(argv[2]);
211 horizontal_padding = atoi(argv[3]);
219 FD_ZERO(&descriptors);
220 FD_SET(fifo_fd, &descriptors);
221 FD_SET(dpy_fd, &descriptors);
223 if (select(sel_fd, &descriptors, NULL, NULL, NULL)) {
224 if (FD_ISSET(dpy_fd, &descriptors)) {
225 while ((evt = xcb_poll_for_event(dpy)) != NULL) {
231 if (FD_ISSET(fifo_fd, &descriptors)) {
232 int bytes = read(fifo_fd, external_infos, sizeof(external_infos));
234 external_infos[bytes] = '\0';