]> git.lizzy.rs Git - bspwm.git/blob - bsps.c
Window title: WM_NAME fallback
[bspwm.git] / bsps.c
1 #define _BSD_SOURCE
2
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <signal.h>
11 #include <xcb/xcb.h>
12 #include <xcb/xcb_event.h>
13 #include <xcb/xcb_icccm.h>
14 #include <xcb/xcb_ewmh.h>
15 #include <cairo/cairo.h>
16 #include "bsps.h"
17
18 #define MAX(A, B)         ((A) > (B) ? (A) : (B))
19
20 #define FIFO_PATH  "BSPWM_FIFO"
21 #define NO_VALUE   " "
22 #define NAME_SEP   "\0"
23 #define FONT_FAMILY "sans-serif"
24 #define FONT_SIZE   11
25 #define HORIZONTAL_PADDING  9
26
27 typedef enum {
28     false,
29     true
30 } bool;
31
32 char *desktop_name;
33 char *window_title;
34 char *font_family ;
35 int font_size = FONT_SIZE;
36 char external_infos[BUFSIZ] = NO_VALUE;
37 xcb_window_t curwin;
38
39 char *fifo_path;
40 int fifo_fd, dpy_fd, sel_fd;
41
42 xcb_connection_t *dpy;
43 xcb_ewmh_connection_t ewmh;
44 xcb_screen_t *screen;
45 int default_screen;
46
47 uint16_t screen_width;
48 unsigned int horizontal_padding = HORIZONTAL_PADDING;
49
50
51 bool running;
52
53 double text_width(char *s)
54 {
55     int w;
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);
62     w = te.x_advance;
63     cairo_destroy(cr);
64     cairo_surface_destroy(surface);
65     /* fprintf(stderr, "%s\n", cairo_status_to_string(cairo_status(cr))); */
66     return w;
67 }
68
69 void handle_signal(int sig)
70 {
71     if (sig == SIGTERM || sig == SIGINT || sig == SIGHUP)
72         running = false;
73 }
74
75 void update_window_title(void)
76 {
77     xcb_window_t win;
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};
82
83     free(window_title);
84     ewmh_txt_prop.strings = NULL;
85     icccm_txt_prop.name = NULL;
86
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);
94         else
95             window_title = strdup(NO_VALUE);
96         if (win != curwin) {
97             xcb_change_window_attributes(dpy, curwin, XCB_CW_EVENT_MASK, values_reset);
98             curwin = win;
99         }
100         xcb_generic_error_t *err = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, win, XCB_CW_EVENT_MASK, values));
101         if (err != NULL)
102             running = false;
103     } else {
104         window_title = strdup(NO_VALUE);
105     }
106 }
107
108 void update_desktop_name(void)
109 {
110     uint32_t cd;
111     xcb_ewmh_get_utf8_strings_reply_t names;
112     unsigned int pos = 0, cnt = 0;
113
114     free(desktop_name);
115
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;
119             cnt++;
120         }
121         desktop_name = strdup(names.strings + pos);
122     } else {
123         desktop_name = strdup(NO_VALUE);
124     }
125 }
126
127 void output_infos(void)
128 {
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);
133
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;
137
138     printf("^pa(%i)%s^pa(%i)%s^pa(%i)%s\n", left_pos, desktop_name, center_pos, window_title, right_pos, external_infos);
139     fflush(stdout);
140 }
141
142 void handle_event(xcb_generic_event_t *evt)
143 {
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();
150                 output_infos();
151             } else if (pne->atom == ewmh._NET_ACTIVE_WINDOW) {
152                 update_window_title();
153                 output_infos();
154             } else if (pne->window != screen->root && (pne->atom == ewmh._NET_WM_NAME || pne->atom == XCB_ATOM_WM_NAME)) {
155                 update_window_title();
156                 output_infos();
157             }
158         default:
159             break;
160     }
161 }
162
163 void register_events(void)
164 {
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));
168     if (err != NULL)
169         running = false;
170 }
171
172 void setup(void)
173 {
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);
189     curwin = 0;
190     running = true;
191 }
192
193 int main(int argc, char *argv[])
194 {
195     fd_set descriptors;
196     xcb_generic_event_t *evt;
197     signal(SIGTERM, handle_signal);
198     signal(SIGINT, handle_signal);
199     signal(SIGHUP, handle_signal);
200     setup();
201     register_events();
202     update_desktop_name();
203     update_window_title();
204
205     if (argc > 1) {
206         free(font_family);
207         font_family = strdup(argv[1]);
208         if (argc > 2) {
209             font_size = atoi(argv[2]);
210             if (argc > 3)
211                 horizontal_padding = atoi(argv[3]);
212         }
213     }
214
215     output_infos();
216     xcb_flush(dpy);
217
218     while (running) {
219         FD_ZERO(&descriptors);
220         FD_SET(fifo_fd, &descriptors);
221         FD_SET(dpy_fd, &descriptors);
222
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) {
226                     handle_event(evt);
227                     free(evt);
228                 }
229             }
230
231             if (FD_ISSET(fifo_fd, &descriptors)) {
232                 int bytes = read(fifo_fd, external_infos, sizeof(external_infos));
233                 if (bytes > 0) {
234                     external_infos[bytes] = '\0';
235                     output_infos();
236                 }
237             }
238         }
239     }
240
241     close(fifo_fd);
242     xcb_disconnect(dpy);
243     return 0;
244 }