]> git.lizzy.rs Git - bspwm.git/blob - bsps.c
Trying to prevent the shutdown of cr
[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_ewmh.h>
14 #include <cairo/cairo.h>
15 #include "bsps.h"
16
17 #define MAX(A, B)         ((A) > (B) ? (A) : (B))
18
19 #define FIFO_PATH  "BSPWM_FIFO"
20 #define NO_VALUE   " "
21 #define NAME_SEP   "\0"
22 #define FONT_FAMILY "sans-serif"
23 #define FONT_SIZE   11
24 #define HORIZONTAL_PADDING  9
25
26 typedef enum {
27     false,
28     true
29 } bool;
30
31 char *desktop_name;
32 char *window_title;
33 char *font_family ;
34 int font_size = FONT_SIZE;
35 char external_infos[BUFSIZ] = NO_VALUE;
36 xcb_window_t curwin;
37
38 char *fifo_path;
39 int fifo_fd, dpy_fd, sel_fd;
40
41 xcb_connection_t *dpy;
42 xcb_ewmh_connection_t ewmh;
43 xcb_screen_t *screen;
44 int default_screen;
45
46 uint16_t screen_width;
47 unsigned int horizontal_padding = HORIZONTAL_PADDING;
48
49
50 bool running;
51
52 double text_width(char *s)
53 {
54     int w;
55     cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 1, 1);
56     cairo_t *cr = cairo_create(surface);
57     cairo_text_extents_t te;
58     cairo_select_font_face(cr, font_family, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
59     cairo_set_font_size(cr, font_size);
60     cairo_text_extents(cr, s, &te);
61     w = te.x_advance;
62     cairo_destroy(cr);
63     cairo_surface_destroy(surface);
64     /* fprintf(stderr, "%s\n", cairo_status_to_string(cairo_status(cr))); */
65     return w;
66 }
67
68 void handle_signal(int sig)
69 {
70     if (sig == SIGTERM || sig == SIGINT || sig == SIGHUP)
71         running = false;
72 }
73
74 void update_window_title(void)
75 {
76     xcb_window_t win;
77     xcb_ewmh_get_utf8_strings_reply_t name;
78     uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
79     uint32_t values_reset[] = {XCB_EVENT_MASK_NO_EVENT};
80
81     free(window_title);
82
83     if (xcb_ewmh_get_active_window_reply(&ewmh, xcb_ewmh_get_active_window(&ewmh, default_screen), &win, NULL) == 1
84             && xcb_ewmh_get_wm_name_reply(&ewmh, xcb_ewmh_get_wm_name(&ewmh, win), &name, NULL) == 1) {
85         window_title = strdup(name.strings);
86         if (win != curwin) {
87             xcb_change_window_attributes(dpy, curwin, XCB_CW_EVENT_MASK, values_reset);
88             curwin = win;
89         }
90         xcb_generic_error_t *err = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, win, XCB_CW_EVENT_MASK, values));
91         if (err != NULL)
92             running = false;
93
94     } else {
95         window_title = strdup(NO_VALUE);
96     }
97 }
98
99 void update_desktop_name(void)
100 {
101     uint32_t cd;
102     xcb_ewmh_get_utf8_strings_reply_t names;
103     unsigned int pos = 0, cnt = 0;
104
105     free(desktop_name);
106
107     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) {
108         while (cnt < cd && pos < names.strings_len) {
109             pos += strlen(names.strings + pos) + 1;
110             cnt++;
111         }
112         desktop_name = strdup(names.strings + pos);
113     } else {
114         desktop_name = strdup(NO_VALUE);
115     }
116 }
117
118 void output_infos(void)
119 {
120     double left_width = text_width(desktop_name);
121     double center_width = text_width(window_title);
122     double right_width = text_width(external_infos);
123     double available_center = screen_width - (left_width + right_width + 4 * horizontal_padding);
124
125     int left_pos = horizontal_padding;
126     int center_pos = left_width + 2 * horizontal_padding + (available_center / 2) - (center_width / 2);
127     int right_pos = screen_width - right_width - horizontal_padding;
128
129     printf("^pa(%i)%s^pa(%i)%s^pa(%i)%s\n", left_pos, desktop_name, center_pos, window_title, right_pos, external_infos);
130     fflush(stdout);
131 }
132
133 void handle_event(xcb_generic_event_t *evt)
134 {
135     xcb_property_notify_event_t *pne;
136     switch (XCB_EVENT_RESPONSE_TYPE(evt)) {
137         case XCB_PROPERTY_NOTIFY:
138             pne = (xcb_property_notify_event_t *) evt;
139             if (pne->atom == ewmh._NET_CURRENT_DESKTOP || pne->atom == ewmh._NET_DESKTOP_NAMES) {
140                 update_desktop_name();
141                 output_infos();
142             } else if (pne->atom == ewmh._NET_ACTIVE_WINDOW) {
143                 update_window_title();
144                 output_infos();
145             } else if (pne->window != screen->root && (pne->atom == ewmh._NET_WM_NAME || pne->atom == XCB_ATOM_WM_NAME)) {
146                 update_window_title();
147                 output_infos();
148             }
149         default:
150             break;
151     }
152 }
153
154 void register_events(void)
155 {
156     xcb_generic_error_t *err;
157     uint32_t values[] = {XCB_EVENT_MASK_PROPERTY_CHANGE};
158     err = xcb_request_check(dpy, xcb_change_window_attributes_checked(dpy, screen->root, XCB_CW_EVENT_MASK, values));
159     if (err != NULL)
160         running = false;
161 }
162
163 void setup(void)
164 {
165     dpy = xcb_connect(NULL, &default_screen);
166     xcb_intern_atom_cookie_t *ewmh_cookies;
167     ewmh_cookies = xcb_ewmh_init_atoms(dpy, &ewmh);
168     xcb_ewmh_init_atoms_replies(&ewmh, ewmh_cookies, NULL);
169     screen = ewmh.screens[default_screen];
170     screen_width = screen->width_in_pixels;
171     fifo_path = getenv(FIFO_PATH);
172     mkfifo(fifo_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
173     /* http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ */
174     fifo_fd = open(fifo_path, O_RDWR | O_NONBLOCK);
175     dpy_fd = xcb_get_file_descriptor(dpy);
176     sel_fd = MAX(fifo_fd, dpy_fd) + 1;
177     desktop_name = strdup(NO_VALUE);
178     window_title = strdup(NO_VALUE);
179     font_family = strdup(FONT_FAMILY);
180     curwin = 0;
181     running = true;
182 }
183
184 int main(int argc, char *argv[])
185 {
186     fd_set descriptors;
187     xcb_generic_event_t *evt;
188     signal(SIGTERM, handle_signal);
189     signal(SIGINT, handle_signal);
190     signal(SIGHUP, handle_signal);
191     setup();
192     register_events();
193     update_desktop_name();
194     update_window_title();
195
196     if (argc > 1) {
197         free(font_family);
198         font_family = strdup(argv[1]);
199         if (argc > 2) {
200             font_size = atoi(argv[2]);
201             if (argc > 3)
202                 horizontal_padding = atoi(argv[3]);
203         }
204     }
205
206     output_infos();
207     xcb_flush(dpy);
208
209     while (running) {
210         FD_ZERO(&descriptors);
211         FD_SET(fifo_fd, &descriptors);
212         FD_SET(dpy_fd, &descriptors);
213
214         if (select(sel_fd, &descriptors, NULL, NULL, NULL)) {
215             if (FD_ISSET(dpy_fd, &descriptors)) {
216                 while ((evt = xcb_poll_for_event(dpy)) != NULL) {
217                     handle_event(evt);
218                     free(evt);
219                 }
220             }
221
222             if (FD_ISSET(fifo_fd, &descriptors)) {
223                 int bytes = read(fifo_fd, external_infos, sizeof(external_infos));
224                 if (bytes > 0) {
225                     external_infos[bytes] = '\0';
226                     output_infos();
227                 }
228             }
229         }
230     }
231
232     close(fifo_fd);
233     xcb_disconnect(dpy);
234     return 0;
235 }